// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_engine.h"
#include "mk_render_device.h"
#include "mk_render_define.h"
#include "mk_cegui_texture.h"
#include "mk_resource_manager.h"

#include "CEGUI.h"
#include "CEGUIDefaultResourceProvider.h"

namespace MK3DE
{
	CCEGUITexture::CCEGUITexture(CEGUI::Renderer* owner) :
		CEGUI::Texture(owner), _isMemoryTexture(FALSE), _xScale(1.0f), _yScale(1.0f),
		_texture(NULL)
	{

	}

	CCEGUITexture::~CCEGUITexture()
	{
		__FreeTexture();
	}

	CEGUI::ushort CCEGUITexture::getWidth(void) const
	{
		return (CEGUI::ushort)_texture->GetWidth();
	}

	CEGUI::ushort CCEGUITexture::getOriginalWidth(void) const
	{
		return (CEGUI::ushort)_texture->GetOriginalWidth();
	}

	float CCEGUITexture::getXScale(void) const
	{
		return _xScale;
	}

	CEGUI::ushort CCEGUITexture::getHeight(void) const
	{
		return (CEGUI::ushort)_texture->GetHeight();
	}

	CEGUI::ushort CCEGUITexture::getOriginalHeight(void) const
	{
		return (CEGUI::ushort)_texture->GetOriginalHeight();
	}

	float CCEGUITexture::getYScale(void) const
	{
		return _yScale;
	}

	void CCEGUITexture::__FreeTexture()
	{
		if (_texture)
			GetEngine()->GetRenderDevice()->DestroyTexture2D(_texture);
	}

	void CCEGUITexture::__UpdateCachedScaleValues()
	{
		CEGUI::ushort orgW = getOriginalWidth();
		CEGUI::ushort texW = getWidth();

		_xScale = 1.0f / ((orgW == texW) ? (float)orgW : (float)texW);

		CEGUI::ushort orgH = getOriginalHeight();
		CEGUI::ushort texH = getHeight();

		_yScale = 1.0f / ((orgH == texH) ? (float)orgH : (float)texH);
	}

	void CCEGUITexture::loadFromFile(const CEGUI::String& filename, const CEGUI::String& resourceGroup)
	{
		__FreeTexture();

		CEGUI::RawDataContainer texFile;
		CEGUI::System::getSingleton().getResourceProvider()->loadRawDataContainer(filename, texFile, resourceGroup);
		BOOL ret = GetEngine()->GetResourceManager()->GetTexture2DFromMemory(texFile.getDataPtr(), (DWORD)texFile.getSize(), _texture);
		CEGUI::System::getSingleton().getResourceProvider()->unloadRawDataContainer(texFile);

		if (ret)
		{
			_fileName = filename;
			_resourceGroup = resourceGroup;
			_isMemoryTexture = FALSE;
		}
		else
		{
			throw CEGUI::RendererException("Failed to create Texture object from file '" + filename + "'.");
		}
	}

	void CCEGUITexture::loadFromMemory(const void* buffPtr, CEGUI::uint buffWidth, CEGUI::uint buffHeight, CEGUI::Texture::PixelFormat pixelFormat)
	{
		__FreeTexture();

		CEGUI::uint tex_size = ceguimax(buffWidth, buffHeight);

		PIXEL_FORMAT pixFmt;
		switch (pixelFormat)
		{
		case CEGUI::Texture::PF_RGB:
			pixFmt = PF_X8R8G8B8;
			break;

		case CEGUI::Texture::PF_RGBA:
			pixFmt = PF_A8R8G8B8;
			break;

		default:
			throw CEGUI::RendererException("Failed to load texture from memory: Invalid pixelformat.");
		}

		BOOL ret = GetEngine()->GetRenderDevice()->CreateTexture2D((DWORD)tex_size, (DWORD)tex_size, pixFmt, 1, TU_NORMAL, _texture);
		if (!ret)
		{
			throw CEGUI::RendererException("Failed to load texture from memory: Texture creation failed.");
		}
		else
		{
			BYTE* buffer = NULL;
			DWORD pitch = 0;
			ret = _texture->LockRect((void**)&buffer, &pitch, NULL);
			if (!ret)
			{
				throw CEGUI::RendererException("Failed to load texture from memory: lock failed.");
			}
			else
			{
				CEGUI::ulong* dst = (CEGUI::ulong*)buffer;
				CEGUI::ulong* src = (CEGUI::ulong*)buffPtr;

				// RGBA
				if (pixelFormat == CEGUI::Texture::PF_RGBA)
				{
					for (CEGUI::uint i = 0; i < buffHeight; ++i)
					{
						for (CEGUI::uint j = 0; j < buffWidth; ++j)
						{
							// we dont need endian safety on microsoft
							CEGUI::uchar r = (CEGUI::uchar)(src[j] & 0xFF);
							CEGUI::uchar g = (CEGUI::uchar)((src[j] >> 8) & 0xFF);
							CEGUI::uchar b = (CEGUI::uchar)((src[j] >> 16)  & 0xFF);
							CEGUI::uchar a = (CEGUI::uchar)((src[j] >> 24) & 0xFF);

							dst[j] = MK_COLOR_ARGB(a, r, g, b);
						}

						dst += pitch / sizeof(CEGUI::ulong);
						src += buffWidth;
					}
				}
				// RGB
				else
				{
					for (CEGUI::uint i = 0; i < buffHeight; ++i)
					{
						for (CEGUI::uint j = 0; j < buffWidth; ++j)
						{
							dst[j] = src[j];
						}

						dst += pitch / sizeof(CEGUI::ulong);
						src += buffWidth;
					}
				}

				_texture->Unlock();
			}
		}
	}

	BOOL CCEGUITexture::_CreateTexture(DWORD size)
	{
		__FreeTexture();
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		return rd->CreateTexture2D(size, size, PF_A8R8G8B8, 1, TU_NORMAL, _texture);
	}

	BOOL CCEGUITexture::_CreateTexture(const char* fileName)
	{
		__FreeTexture();
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		return rd->CreateTexture2DFromFile(fileName, _texture);
	}   
}